package archer.framework.security.token.filter;

import archer.framework.protocol.result.ExecuteResultProcessor;
import archer.framework.security.utils.TokenUtils;
import archer.framework.utils.FilterUtils;
import archer.framework.utils.JSONUtils;
import archer.framework.utils.ServletUtils;
import archer.framework.utils.ValidateUtils;
import jodd.util.Wildcard;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import java.io.IOException;

/**
 * 登录认证过滤器
 *
 * @author christ
 * @date 2016/6/8
 */
public class AuthenticateFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(AuthenticateFilter.class);

    private static final String AUTHENTICATED_SIGN = AuthenticateFilter.class.getName() + "_AUTHENTICATED_SIGN";

    protected String path;

    private String[] paths;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        FilterUtils.readFilterConfigParams(filterConfig, this, "path");

        if (ValidateUtils.isNotEmpty(path)) {
            paths = path.split(";");
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        // 如果请求不匹配过滤规则，则跳过
        if (!match(servletRequest)) {
            executeChain(servletRequest, servletResponse, filterChain);
            return;
        }

        // 判断当前请求是否需要跳过认证
        if (skipAuthenticate(servletRequest, servletResponse)) {
            executeChain(servletRequest, servletResponse, filterChain);
            return;
        }

        // 进行登录认证，如果失败，则拒绝该请求
        if (!isTokenValidate(servletRequest, servletResponse)) {
            refuse(servletRequest, servletResponse);
            return;
        }

        // 标识当前请求已经通过认证
        authenticated(servletRequest, servletResponse);

        // 执行下一条过滤器
        executeChain(servletRequest, servletResponse, filterChain);
    }

    /**
     * 请求是否匹配过滤规则
     *
     * @param servletRequest
     * @return
     */
    protected boolean match(ServletRequest servletRequest) {

        if (ValidateUtils.isNotEmpty(paths)) {
            for (String each : paths) {
                if (Wildcard.match(ServletUtils.getRequestURI(servletRequest), each)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    protected boolean isTokenValidate(ServletRequest servletRequest, ServletResponse servletResponse) {

        return ValidateUtils.isNotEmpty(TokenUtils.getToken(ServletUtils.getRequest(servletRequest)));
    }

    /**
     * 判断当前请求是否需要登录认证
     *
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    protected boolean skipAuthenticate(ServletRequest servletRequest, ServletResponse servletResponse) {

        if (ValidateUtils.isNotEmpty(servletRequest.getAttribute(AUTHENTICATED_SIGN))) {
            return true;
        }

        return false;
    }

    protected void authenticated(ServletRequest servletRequest, ServletResponse servletResponse) {

        servletRequest.setAttribute(AUTHENTICATED_SIGN, AUTHENTICATED_SIGN);
    }

    /**
     * 执行过滤器链
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    protected void executeChain(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        filterChain.doFilter(servletRequest, servletResponse);
    }

    /**
     * 拒绝
     *
     * @param servletResponse
     * @throws IOException
     * @throws ServletException
     */
    protected void refuse(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException, ServletException {
        logger.info("AUTHENTICATE REFUSE >> " + ServletUtils.getRequestURI(servletRequest));
        servletResponse.setContentType("application/json;charset=UTF-8");
        servletResponse.getWriter().write(JSONUtils.serialize(ExecuteResultProcessor.notLogined()));
        servletResponse.flushBuffer();
    }

    @Override
    public void destroy() {

    }
}
